# ---------------------------------------------------------------------------
# Desc:   PowerShell Community Extensions miscellaneous filter definitions.
# Site:   http://www.codeplex.com/PowerShellCX
# Usage:  In your profile place the following command:
#  
#         . "$Env:PscxHome\Profile\GenericFilters.ps1"
# ---------------------------------------------------------------------------

# ---------------------------------------------------------------------------
# Name:   Get-PropertyValue
# Alias:  gpv
# Author: Keith Hill
# Desc:   This filter is different from the Select-Object cmdlet in that it
#         doesn't create a wrapper object (PSCustomObject) around the property.
#         If you just want to get the property's value to assign it to another
#         variable this filter will come in handy.  If you assigned the result
#         of the Select-Object operation you wouldn't get the property's value.
#         You would get an object that wraps that property and its value.
#         NOTE: I wish Get-ItemProperty used the alias "gip" so that we could
#         just call this Get-Property and use "gp" for an alias.
#        
# Usage:  $start = h -Id 143 | gpv StartExecutionTime
#         $start = Get-History -Id 143 | Get-PropertyValue StartExecutionTime
# ---------------------------------------------------------------------------
set-alias gpv Get-PropertyValue -Option AllScope -Description "PSCX filter alias"
filter Get-PropertyValue([string] $propertyName) 
{
    $_.$propertyName
}

# ---------------------------------------------------------------------------
# Name:   Get-TypeName
# Alias:  gtn
# Author: Keith Hill
# Desc:   This filter is handy for finding out what types are being passed
#         down the pipeline quickly. Normally you would use Get-Member to
#         determine this but if you are only interested in the type name
#         this filter produces much less output.  Also, since Get-Member
#         accumulates multiple instances of the same type into a single output
#         record for that type, you don't get any sense of how many objects of
#         that type are traversing the pipeline.  With this filter, you will
#         see the type name of *every* object passed into it.
#
# Usage:  gcm get-* | gtn
#         Get-Command get-* | Get-TypeName $true
# ---------------------------------------------------------------------------
set-alias gtn Get-TypeName -Option AllScope -Description "PSCX filter alias"
filter Get-TypeName([bool]$fullName=$false) 
{
	if ($_ -eq $null) {
		"<null>"
	}
	else {
		if ($fullName) {
			$_.PSBase.GetType().FullName
		}
		else {
			$_.PSBase.GetType().Name
		}
	}
}

# ---------------------------------------------------------------------------
# Name:   ConvertFrom-WmiDateTime, ConvertFrom-WmiTimeSpan,
#         ConvertTo-DmtfDateTime, ConvertTo-DmtfTimeInterval
# Author: Keith Hill inspired by a Jim Truher post on 
#         microsoft.public.windows.powershell
# Desc:   Uses the PowerShell ManagementDateTimeConverter to converter 
#         between Wmi DateTime/Timespan and Dmtf DateTime/TimeInterval
# Usage:
#   gwmi win32_operatingsystem | gpv LastBootUpTime | ConvertFrom-WmiDateTime
# ---------------------------------------------------------------------------
function ConvertFrom-WmiDateTime($wmiDateTime) {
	process {
		if ($_ -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDateTime($_)
		}
	}
	
	end {
		if ($wmiDateTime -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDateTime($wmiDateTime)
		}
	}
}

function ConvertFrom-WmiTimeSpan($wmiTimeSpan) {
	process {
		if ($_ -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToTimeSpan($_)
		}
	}
	
	end {
		if ($wmiTimeSpan -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToTimeSpan($wmiTimeSpan)
		}
	}
}

function ConvertTo-DmtfDateTime($dateTime) {
	process {
		if ($_ -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($_)
		}
	}
	
	end {
		if ($dateTime -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDmtfDateTime($dateTime)
		}
	}
}

function ConvertTo-DmtfTimeInterval($timeSpan) {
	process {
		if ($_ -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDmtfTimeInterval($_)
		}
	}
	
	end {
		if ($timeSpan -ne $null) {
			[System.Management.ManagementDateTimeConverter]::ToDmtfTimeInterval($timeSpan)
		}
	}
}

# ---------------------------------------------------------------------------
# Name:   Set-ReadOnly, Set-Writable
# Alias:  sro, swr
# Author: Keith Hill
# Desc:   These two filters can be used to change a file's readonly status. 

# Usage:  sro foo.txt
#         sro [a-h]*.txt -passthru
#         gci bar[0-9].txt | Set-ReadOnly
#         swr foo.txt
#         swr [a-h]*.txt -passthru
#         gci bar[0-9].txt | Set-Writable
# ---------------------------------------------------------------------------
set-alias sro Set-ReadOnly -Option AllScope -Description "PSCX filter alias"
function Set-ReadOnly([string[]]$paths, [switch]$passThru=$false) {
	begin {
		function MakeFileReadOnly([string]$path, [bool]$isFilter) {
			$rpaths = Resolve-Path $path
			foreach ($rpath in $rpaths) {
				$file = New-Object System.IO.FileInfo $rpath
				$file.IsReadOnly = $true
				if (!$isFilter -and $passThru) {
					$file
				}
			}
		}
	}
	process {
		if ($_ -is [System.IO.FileInfo]) {
			$_.IsReadOnly = $true
		}
		elseif ($_ -is [String] -and [System.IO.File]::Exists($_)) {
			MakeFileReadOnly $_ $true
		}
		$_
	}
	end {
        if ($paths) {
			foreach ($path in $paths) {
				MakeFileReadOnly $path $false
			}
        }	
	}
}

set-alias swr Set-Writable -Option AllScope -Description "PSCX filter alias"
function Set-Writable([string[]]$paths, [switch]$passThru=$false) {
	begin {
		function MakeFileReadOnly([string]$path, [bool]$isFilter) {
			$rpaths = Resolve-Path $path
			foreach ($rpath in $rpaths) {
				$file = New-Object System.IO.FileInfo $rpath
				$file.IsReadOnly = $false
				if (!$isFilter -and $passThru) {
					$file
				}
			}
		}
	}
	process {
		if ($_ -is [System.IO.FileInfo]) {
			$_.IsReadOnly = $false
		}
		elseif ($_ -is [String] -and [System.IO.File]::Exists($_)) {
			MakeFileReadOnly $_ $true
		}
		$_
	}
	end {
        if ($paths) {
			foreach ($path in $paths) {
				MakeFileReadOnly $path $false
			}
        }	
	}
}

# ---------------------------------------------------------------------------
# Name:   Remove-Accessors
# Alias:  ra
# Author: Keith Hill
# Desc:   This filter is intended to be used to filter the Get-Member output
#         to eliminate display of many duplicate accessor methods.
#        
# Usage:  gps | gm | ra
#         Get-Process | Get-Member | Remove-Accessors
# ---------------------------------------------------------------------------
set-alias ra Remove-Accessors -Option AllScope -Description "PSCX filter alias"
filter Remove-Accessors 
{
    if ($_.Name -notmatch "(get_*)|(set_*)|(op_*)") { 
        $_ 
    }
}

# ---------------------------------------------------------------------------
# Name:   Add-DirectoryLength
# Alias:  adl
# Author: Keith Hill
# Desc:   Calculates the sizes of the specified directory and adds that size
#         as a "Length" NoteProperty to the input DirectoryInfo object.
# Usage:  Get-ChildItem . -recurse | Add-DirectoryLength | sort Length
#         dir | adl
# ---------------------------------------------------------------------------
set-alias adl Add-DirectoryLength -Option AllScope -Description "PSCX filter alias"
filter Add-DirectoryLength {
	function processFile([string]$path) {
		(get-item -LiteralPath $path -Force -ea Continue).Length
	}
	
	function processDirectory([string]$path) {
		$items = get-childitem -LiteralPath $path -Force -ea Continue | sort @{e={$_.PSIsContainer}}
		if ($items -eq $null) {
			return $null
		}
		foreach ($item in $items) {
			if ($item.PSIsContainer) {
				$dirSize += processDirectory($item.FullName)
			}
			else {
				$dirSize += processFile($item.FullName)
			}
		}
		$dirSize
	}

	if ($_ -is [System.IO.DirectoryInfo]) {
		$dirSize = processDirectory($_.FullName)
		Add-Member NoteProperty Length $dirSize -InputObject $_
	}
	$_
}

# ---------------------------------------------------------------------------
# Name:   Add-ShortPath
# Alias:  asp
# Author: Keith Hill
# Desc:   Adds the file or directory's short path as a "ShortPath" 
#         NoteProperty to the input DirectoryInfo object. NOTE: This filter 
#         requires the PSCX cmdlet Get-ShortPath
# Usage:  dir | Add-ShortPath | Format-Table ShortPath,FullName
#         dir | asp | ft ShortPath,Name -auto
# ---------------------------------------------------------------------------
set-alias asp Add-ShortPath -Option AllScope -Description "PSCX filter alias"
filter Add-ShortPath {
	if ($_ -is [System.IO.FileSystemInfo]) {
		$shortPathInfo = Get-ShortPath -LiteralPath $_.Fullname 
		Add-Member NoteProperty ShortPath $shortPathInfo.ShortPath -InputObject $_
	}
	$_
}

# ---------------------------------------------------------------------------
# Name:   Invoke-Ternary
# Alias:  ?:
# Author: Karl Prosser
# Desc:   Similar to the C# ? : operator e.g. 
#            _name = (value != null) ? String.Empty : value;
# Usage:  1..10 | ?: {$_ -gt 5} {"Greater than 5;$_} {"Not greater than 5";$_}
# ---------------------------------------------------------------------------
set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
	if (&$decider) { 
		&$ifTrue
	} 
	else { 
		&$ifFalse 
	}
}

# ---------------------------------------------------------------------------
# Name:   Invoke-NullAlt
# Alias:  ??
# Author: Keith Hill but heavily influenced by Karl Prosser's Invoke-Ternary
# Desc:   Similar to the C# ?? operator e.g. 
#         _name = value ?? String.Empty;
#         where value would be a Nullable<T> in C#.  Even though PowerShell
#         doesn't support nullables yet (AFAICT) we can approximate this behavior.
#         In the example below, $LogDir will be assigned the value of $env:LogDir
#         if it exists and it's not null, otherwise it get's assigned the
#         result of the second script block (C:\Windows\System32\LogFiles).
#         This behavior is also analgous to KornShell assignments of this form:
#         LogDir = ${$LogDir:-$WinDir/System32/LogFiles}
#
# Usage:  $LogDir = ?? {$env:LogDir} {"$env:windir\System32\LogFiles"}
# ---------------------------------------------------------------------------
# Usage: 
set-alias ?? Invoke-NullAlt -Option AllScope -Description "PSCX filter alias"
filter Invoke-NullAlt ([scriptblock]$expression, [scriptblock]$executeIfNull) 
{
	if ($expression -ne $null) {
		$result = &$expression
		if ($result -ne $null) {
			$result
		}
		else {
			&$executeIfNull
		}
	}
	else {
		&$executeIfNull
	}
}